package com.yangxu.hospital.system.modules.system.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alipay.api.AlipayApiErrorEnum;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.internal.util.AlipaySignature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.yangxu.hospital.common.annotation.AnonymousAccess;
import com.yangxu.hospital.common.annotation.rest.AnonymousGetMapping;
import com.yangxu.hospital.common.utils.RedisUtils;
import com.yangxu.hospital.common.utils.SecurityUtils;
import com.yangxu.hospital.common.utils.StringUtils;
import com.yangxu.hospital.logging.annotation.Log;
import com.yangxu.hospital.system.modules.system.domain.*;

import com.yangxu.hospital.system.modules.system.domain.dto.AppointmentDto;
import com.yangxu.hospital.system.modules.system.domain.dto.SysPatientDto;
import com.yangxu.hospital.system.modules.system.domain.dto.UserDto;
import com.yangxu.hospital.system.modules.system.domain.vo.AppointmentQueryCriteria;
import com.yangxu.hospital.system.modules.system.mapper.AppointmentMapper;
import com.yangxu.hospital.system.modules.system.service.*;
import com.yangxu.hospital.tools.domain.AlipayConfig;
import com.yangxu.hospital.tools.domain.vo.TradeVo;
import com.yangxu.hospital.tools.service.AliPayService;
import com.yangxu.hospital.tools.utils.AliPayStatusEnum;
import com.yangxu.hospital.tools.utils.AlipayUtils;
import com.yangxu.hospital.tools.utils.MailUtil;
import com.yangxu.hospital.tools.utils.TimeUtils;
import io.swagger.annotations.ApiOperation;
import lombok.Getter;
import lombok.Setter;
import net.dreamlu.mica.core.result.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/h5")
public class AlipayController {

//    @Autowired
//    private AlipayService alipayService;
    @Autowired
    private AlipayUtils alipayUtils;
    @Autowired
    private  RedisUtils redisUtils;
    @Autowired
    private  AliPayService alipayService;

    @Autowired
    private SysDepartmentService sysDepartmentService;

    @Autowired
    private AppointmentSlotsService appointmentSlotsService;

    @Autowired
    private AppointmentService appointmentService;

    @Autowired
    private SysDoctorService doctorService;
    @Autowired
    private AppointmentMapper appointmentMapper;

    @Autowired
    private SysOrderService sysOrderService;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private UserService userService;

    /**
     * 支付宝授权登录 获取用户信息
     * @param request
     * @return
     */
//    @PostMapping("/login")
//    public ResponseEntity<?> alipayLogin(@RequestBody AlipayLoginRequest request) {
//        try {
//            return alipayService.loginWithAuthCode(request.getAuthCode());
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        }
//    }


    /**
     * 解密数据 获取手机号 用不到没有资格
     * @return
     */
    @ApiOperation(value = "解密数据")
    @PostMapping("/findPhone")
    public String findPhone(@RequestBody String jsonStr) throws Exception {

        //小程序前端提交的加密数据
        String response = JSON.parseObject(jsonStr).getString("response");

        //1. 获取验签和解密所需要的参数
        Map<String, String> openapiResult = JSON.parseObject(response,
                new TypeReference<Map<String, String>>() {
                }, Feature.OrderedField);
        String signType = "RSA2";
        String charset = "UTF-8";
        String encryptType = "AES";
        String sign = openapiResult.get("sign");
        String content = openapiResult.get("response");

        //如果密文的
        boolean isDataEncrypted = !content.startsWith("{");
        boolean signCheckPass = false;

        //2. 验签
        String signContent = content;
        //你的小程序对应的支付宝公钥（为扩展考虑建议用appId+signType做密钥存储隔离）
        String signVeriKey = "";
        //你的小程序对应的加解密密钥（为扩展考虑建议用appId+encryptType做密钥存储隔离）
        String decryptKey = "";

        //如果是加密的报文则需要在密文的前后添加双引号
        if (isDataEncrypted) {
            signContent = "\"" + signContent + "\"";
        }
        try {
            signCheckPass = AlipaySignature.rsaCheck(signContent, sign, signVeriKey, charset, signType);
        } catch (AlipayApiException e) {
            //验签异常, 日志
        }
        if(!signCheckPass) {
            //验签不通过（异常或者报文被篡改），终止流程（不需要做解密）
            throw new Exception("验签失败");
        }

        //3. 解密
        String plainData = null;
        if (isDataEncrypted) {
            try {
                plainData = AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
            } catch (AlipayApiException e) {
                //解密异常, 记录日志
                throw new Exception("解密异常");
            }
        } else {
            plainData = content;
        }
        return plainData;
    }




    /**
     * todo 获取科室信息
     */
    @ApiOperation(value = "获取科室信息")
    @GetMapping("/getDepartment")
    public ResponseEntity<?> getDepartment() {
        return ResponseEntity.ok(sysDepartmentService.list());
    }


    /**
     * todo 获取医生信息
     */
    @ApiOperation(value = "获取现在时间值班医生的信息")
    @GetMapping("/getDoctor")
    public ResponseEntity<?> getDoctor(@RequestParam String fullDate,String departmentId) {
        System.out.println(fullDate);
        System.out.println(departmentId);

        return ResponseEntity.ok(doctorService.toDaylist(fullDate,departmentId));
//        return ResponseEntity.ok(null);
    }

    /**
     * 获取14天的时间
     */
    @GetMapping("/getWeek")
    public ResponseEntity<?> getWeek(){
        return ResponseEntity.ok(TimeUtils.getNext14DaysWithDayOfWeek());
    }

    /**
     * 获取排号
     * @param fullDate
     * @param departmentId
     * @param doctorId
     * @return
     */
    @GetMapping("/getSlots")
    public ResponseEntity<?> getSlots(@RequestParam String fullDate,
                                      @RequestParam String departmentId,
                                      @RequestParam String doctorId) {
        LambdaQueryWrapper<AppointmentSlots> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AppointmentSlots::getDoctorId, doctorId)
                .eq(AppointmentSlots::getDepartmentId, departmentId)
                .eq(AppointmentSlots::getScheduleDate, fullDate)
                .orderByAsc(AppointmentSlots::getRowNum)
                .orderByAsc(AppointmentSlots::getAppointmentTime);

        List<AppointmentSlots> list = appointmentSlotsService.list(queryWrapper);

        // 将字符串日期转换为 LocalDate 对象
        LocalDate date = LocalDate.parse(fullDate);
        // 获取当前日期
        LocalDate today = LocalDate.now(ZoneId.systemDefault());
        // 获取当前时间
        LocalTime now = LocalTime.now();

        // 如果 fullDate 是今天，则过滤掉当前时间之前的预约时段
        if (date.equals(today)) {
            List<AppointmentSlots> collect = list.stream()
                    .filter(e -> e.getPatientId() == null)
                    .filter(e -> LocalTime.parse(e.getAppointmentTime()).isAfter(now))
                    .collect(Collectors.toList());
            return ResponseEntity.ok(collect);
        } else {
            // 如果不是今天，只过滤掉未分配病人的预约时段
            list = list.stream()
                    .filter(e -> e.getPatientId() == null)
                    .collect(Collectors.toList());
            return ResponseEntity.ok(list);
        }
    }

    /**
     * 患者预约
     * @param appointment
     * @return
     */
    @PostMapping("/appointment")
    @Transactional(isolation = Isolation.SERIALIZABLE) // 使用最高的事务隔离级别
    public ResponseEntity<?> appointment(@RequestBody AppointmentSlots appointment) {
        Long currentUserId = SecurityUtils.getCurrentUserId();

        // 检查这个日期这个用户在这个部门是否有预约
        LambdaQueryWrapper<AppointmentSlots> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AppointmentSlots::getPatientId, currentUserId)
                .eq(AppointmentSlots::getDepartmentId, appointment.getDepartmentId())
                .eq(AppointmentSlots::getScheduleDate, appointment.getScheduleDate());

        boolean hasAppointment = appointmentSlotsService.count(queryWrapper) > 0;
        if (hasAppointment) {
            return ResponseEntity.ok("您已经预约过了");
        }


        // 获取特定预约槽位并尝试更新
        AppointmentSlots slot = appointmentSlotsService.getOne(
                new LambdaQueryWrapper<AppointmentSlots>()
                        .eq(AppointmentSlots::getId, appointment.getId())
                        .isNull(AppointmentSlots::getPatientId), // 只选择 patient_id 为空的记录
                true // 开启悲观锁
        );

        if (slot == null) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body("预约失败，该时间段已被占用");
        }

        // 更新预约信息，设置 patient_id
        appointment.setPatientId(currentUserId);
        boolean updateSuccess = appointmentSlotsService.updateById(appointment);

        if (!updateSuccess) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("预约失败，请稍后再试");
        }

        // ... 设置预约记录的属性
        // 插入预约记录
        Appointment appointmentRecord = new Appointment();
        appointmentRecord.setPatientId(appointment.getPatientId());
        appointmentRecord.setDoctorId(appointment.getDoctorId());
        appointmentRecord.setAppointmentSlotsId(appointment.getId());
        appointmentRecord.setDepartmentId(appointment.getDepartmentId());

        User doctor = userService.getById(appointment.getDoctorId());
        SysDepartment sysDepartment = sysDepartmentService.getById(appointment.getDepartmentId());


        // 设置预约时间
        LocalDateTime dateTime = LocalDateTime.of(slot.getScheduleDate(), LocalTime.parse(slot.getAppointmentTime()));
        Timestamp timestamp = Timestamp.valueOf(dateTime);
        appointmentRecord.setAppointmentTime(timestamp); // 假设存在此方法
        appointmentService.create(appointmentRecord);
        // 发送预约成功邮件通知
        sendAppointmentConfirmationEmail(currentUserId);
        User user = userService.getById(currentUserId);
        if (user != null || org.springframework.util.StringUtils.hasText(user.getEmail())){
            mailUtil.sendMailMessage1(
                    user.getEmail(),
                    "您在" + LocalDate.now() + "的预约已成功",
                    sysDepartment.getName(),doctor.getNickName(), appointment.getScheduleDate()+" "+slot.getAppointmentTime()

            );
        }
        return ResponseEntity.ok("预约成功");
    }

    /**
     *
     * @param currentUserId
     */
    private void sendAppointmentConfirmationEmail(Long currentUserId) {
        User user = userService.getById(currentUserId);
        if (user != null && StringUtils.isNotBlank(user.getEmail())) {
            mailUtil.sendMailMessage(
                    user.getEmail(),
                    "您在" + LocalDate.now() + "的预约已成功",
                    "预约成功，待支付"
            );
        }
    }

    /**
     * 获取预约信息
     */
    @GetMapping("/getAppointment")
    public ResponseEntity<?> getAppointment(@RequestParam Long id) {
        AppointmentDto byId = appointmentService.findById(id);
        byId.getDoctorId();
        //查找医生
        SysDoctor byId1 = doctorService.getById(byId.getDoctorId());
        byId.setFee(byId1.getFee());
        return ResponseEntity.ok(byId);
    }
//    @PostMapping("/appointment")
//    public ResponseEntity<?> appointment(@RequestBody AppointmentSlots appointment){
//        // 判断这个日期这个用户在这个部门有挂号吗
//        Long currentUserId = SecurityUtils.getCurrentUserId();
//        LambdaQueryWrapper<AppointmentSlots> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(AppointmentSlots::getPatientId, currentUserId)
//                .eq(AppointmentSlots::getDepartmentId, appointment.getDepartmentId())
//                .eq(AppointmentSlots::getScheduleDate, appointment.getScheduleDate());
////        AppointmentSlots one = appointmentSlotsService.getById(appointment);
//        AppointmentSlots one = appointmentSlotsService.getOne(queryWrapper,true);
//        if (one != null && one.getPatientId() != null){
//            if (one.getPatientId().equals(currentUserId)){
//                return ResponseEntity.ok("您已经预约过了");
//            }else {
//                return ResponseEntity.ok("该时间段已被预约");
//            }
//
//        }
////        appointment.setPatientId(currentUserId);
//
//        appointmentSlotsService.updateById(appointment);
//        AppointmentSlots byId = appointmentSlotsService.getById(appointment.getId());
//        LocalDate scheduleDate = byId.getScheduleDate();
//        String appointmentTimeString = byId.getAppointmentTime();
//
//        // 将字符串转换为 LocalTime 对象
//        LocalTime appointmentTime = LocalTime.parse(appointmentTimeString);
//
//        // 将 LocalDate 和 LocalTime 结合为 LocalDateTime
//        LocalDateTime dateTime = LocalDateTime.of(scheduleDate, appointmentTime);
//
//        // 将 LocalDateTime 转换为 Timestamp
//        Timestamp timestamp = Timestamp.valueOf(dateTime);
//        Appointment appointment1 = new Appointment();
//        appointment1.setPatientId(appointment.getPatientId());
//        appointment1.setDoctorId(appointment.getDoctorId());
//        appointment1.setAppointmentSlotsId(appointment.getId());
//        appointment1.setAppointmentTime(timestamp);
//        appointment1.setDepartmentId(appointment.getDepartmentId());
//        // 插入预约信息
//        appointmentService.create(appointment1);
//
//        // 发送邮箱信息
//        Long currentUserId1 = SecurityUtils.getCurrentUserId();
//        User byId2 = userService.getById(currentUserId1);
//
//        mailUtil.sendMailMessage(byId2.getEmail(),"您在"+appointment.getScheduleDate()+"的已预约成功","预约成功,待支付");
//
//        return ResponseEntity.ok("预约成功");
//    }

    /**
     * 取消预约
     */
    @PostMapping("/cancelAppointment")
    public ResponseEntity<?> cancelAppointment(@RequestBody Map<String,Object> objectMap){
        Integer s = (Integer) objectMap.get("appointmentSlotsId");
        Integer patientId = (Integer) objectMap.get("patientId");
        String appointmentTime = (String) objectMap.get("appointmentTime");
        String departmentName = (String) objectMap.get("departmentName");
        String doctorName = (String) objectMap.get("doctorName");
        Long appointmentSlotsId = Long.valueOf(s);
        AppointmentSlots appointment = appointmentSlotsService.getById(appointmentSlotsId);
        appointment.setPatientId(null);
        // 取消预约，预约表也要修改状态
        LambdaUpdateWrapper<AppointmentSlots> appointmentSlotsLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        appointmentSlotsLambdaUpdateWrapper.eq(AppointmentSlots::getId, appointmentSlotsId)
        .set(AppointmentSlots::getPatientId, null);
        appointmentSlotsService.update(appointment,appointmentSlotsLambdaUpdateWrapper);

        // 修改预约表
        Integer appointmentId = (Integer) objectMap.get("appointmentId");
        Long appointmentId1 = Long.valueOf(appointmentId);
        Appointment appointment1 = appointmentService.getById(appointmentId1);
        appointment1.setStatus(2);
        appointmentService.updateById(appointment1);

        AlipayConfig alipay = alipayService.find();
        //
        try{
            // 获取订单号
            SysOrder one = sysOrderService.getOne(new LambdaQueryWrapper<SysOrder>().eq(SysOrder::getAppointmentId, appointmentId));
            if (null != one){
                alipayService.refund(alipay,one.getThirdPartyTransactionId(),one.getTotalAmount());

            }
            // 获取用户信息，发邮箱
            Long currentUserId1 = SecurityUtils.getCurrentUserId();
            User byId2 = userService.getById(currentUserId1);
            mailUtil.sendMailMessage1(byId2.getEmail(),"您在"+appointment.getScheduleDate()+"的预约已取消",departmentName,doctorName,appointmentTime);
        }catch (Exception e){
            e.printStackTrace();

        }
        return ResponseEntity.ok("取消成功");
    }

    @Log("支付宝手机网页支付")
    @ApiOperation("手机网页支付")
    @PostMapping(value = "/toPay")
    public ResponseEntity<String> toPayAsWeb(@Validated @RequestBody Map<String,Object> objectMap) throws Exception {
        Integer appointmentId = (Integer) objectMap.get("appointmentId");
        // 科室名称
        String departmentName = (String) objectMap.get("departmentName");
        // 医生名字
        String doctorName = (String) objectMap.get("doctorName");
        // 预约时间
        String appointmentTime = (String) objectMap.get("appointmentTime");

        Integer doctorId = (Integer) objectMap.get("doctorId");

        Long appointmentId1 = Long.valueOf(appointmentId);

        SysDoctor byId = doctorService.getById(doctorId);
        // 获取费用
        Integer fee = byId.getFee();
        // 设置支付宝支付的信息
        TradeVo trade = new TradeVo();
        trade.setTotalAmount(fee.toString());
        trade.setSubject(departmentName+doctorName+appointmentTime);
        trade.setBody(departmentName+doctorName);
        trade.setReturnUrl("http://api.jsmao.cn:8000/api/h5/return");

        // 当前患者id
        Long currentUserId = SecurityUtils.getCurrentUserId();

        // 查找支付宝的支付配置
        AlipayConfig alipay = alipayService.find();
        // 生成订单号
        String orderCode = alipayUtils.getOrderCode();
        trade.setOutTradeNo(orderCode);
        SysOrder sysOrder = new SysOrder();
        sysOrder.setAppointmentId(appointmentId1);
        sysOrder.setUserId(currentUserId);
        sysOrder.setOrderNumber(orderCode);
        sysOrder.setTotalAmount(BigDecimal.valueOf(fee));
        sysOrder.setPaymentMethod("支付宝");
        sysOrder.setPaymentStatus(0);
        sysOrderService.create(sysOrder);
        String payUrl = alipayService.toPayAsWeb(alipay, trade);
        return ResponseEntity.ok(payUrl);
    }


    /**
     * 订单取消退款
     * @return
     */
    @RequestMapping("/refund")
    @ResponseBody
    public R refund(String bizPayNo){
        AlipayConfig alipay = alipayService.find();
        try{
            alipayService.refund(alipay,bizPayNo,null);
            return R.success();
        }catch (Exception e){
            e.printStackTrace();
            return R.fail("退款失败");
        }
    }
    /**
     * 获取挂号记录
     *
     */
    @GetMapping("/getAppointments")
    public ResponseEntity<?> getAppointments() {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        List<AppointmentDto> list = appointmentMapper.getList(currentUserId);
        return ResponseEntity.ok(list);
    }

    @ApiIgnore
    @AnonymousGetMapping("/return")
    @ApiOperation("支付之后跳转的链接")
    public ResponseEntity<String> returnPage(HttpServletRequest request, HttpServletResponse response) {
        AlipayConfig alipay = alipayService.find();
        response.setContentType("text/html;charset=" + alipay.getCharset());
        //内容验签，防止黑客篡改参数
        if (alipayUtils.rsaCheck(request, alipay)) {
            //商户订单号
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            SysOrder sysOrder = new SysOrder();

            //支付宝交易号
            String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            System.out.println("商户订单号" + outTradeNo + "  " + "第三方交易号" + tradeNo);

            // 根据业务需要返回数据，这里统一返回OK
            return new ResponseEntity<>("支付成功", HttpStatus.OK);
        } else {
            // 根据业务需要返回数据
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }
    }


    @ApiIgnore
    @RequestMapping("/notify")
    @AnonymousAccess
    @ApiOperation("支付异步通知(要公网访问)，接收异步通知，检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致，根据trade_status进行后续业务处理")
    public ResponseEntity<Object> notify(HttpServletRequest request) {
        AlipayConfig alipay = alipayService.find();
        Map<String, String[]> parameterMap = request.getParameterMap();
        //内容验签，防止黑客篡改参数
        if (alipayUtils.rsaCheck(request, alipay)) {
            //交易状态
            String tradeStatus = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            // 商户订单号
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            SysOrder one = sysOrderService.getOne(new LambdaQueryWrapper<SysOrder>().eq(SysOrder::getOrderNumber, outTradeNo));
            System.out.println(one);
            //支付宝交易号
            String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            //付款金额
            String totalAmount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            //验证
            if (tradeStatus.equals(AliPayStatusEnum.SUCCESS.getValue()) || tradeStatus.equals(AliPayStatusEnum.FINISHED.getValue())) {
                // 验证通过后应该根据业务需要处理订单
                one.setPaymentStatus(1);
                one.setThirdPartyTransactionId(tradeNo);
                sysOrderService.update(one);
                Appointment one1 = appointmentService.getOne(new LambdaQueryWrapper<Appointment>().eq(Appointment::getAppointmentId, one.getAppointmentId()));
                // 修改预约成功的状态
                one1.setStatus(1);
                appointmentService.update(one1);
            }
            // 交易关闭 就是退款
            if (tradeStatus.equals(AliPayStatusEnum.CLOSED.getValue())){
                one.setRefundStatus(2);
                one.setRefundAmount(one.getTotalAmount());
                one.setRefundReason("取消预约挂号");
                sysOrderService.update(one);
            }
            return new ResponseEntity<>(HttpStatus.OK);
        }
        return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
//
//    @Log("支付宝手机网页支付")
//    @ApiOperation("手机网页支付")
//    @PostMapping(value = "/toPayAsWeb")
//    public ResponseEntity<String> toPayAsWeb(@Validated @RequestBody TradeVo trade) throws Exception {
//        AlipayConfig alipay = alipayService.find();
//        trade.setOutTradeNo(alipayUtils.getOrderCode());
//        String payUrl = alipayService.toPayAsWeb(alipay, trade);
//        return ResponseEntity.ok(payUrl);
//    }

//    @PostMapping("/login")
//    public ResponseEntity<?> alipayLogin(@RequestBody User user) {
//        try {
//            return ResponseEntity.ok(null);
//        } catch (Exception e) {
//            e.printStackTrace();
//            return null;
//        }
//    }




}

@Getter
@Setter
class AlipayLoginRequest {
    private String authCode;

}


